'''A plugin that runs a webserver and host files you want to send on it'''
#   This file is part of emesene.
#
#    WebSender plugin is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    WebSender plugin is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with emesene; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

VERSION = '0.1'
import os
import random
import thread
import copy
import shutil
import sys
import re

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import cgi
#import mimetypes #TODO

import gtk
import Plugin
import ConversationManager
import urllib2
import paths

default_page = '''<html><title>File not found</title>
<body><h1>File not found</h1>Are you sure?</body></html>'''

style = '''<style type="text/css">
<!--
body {margin:0; border:0; padding:5px; background-color:white;font-size:1.1em;}
#wrap {width:500px; margin:0; border:0; padding:0px; margin:auto;}
#wrapform {height:100px;padding:50px 20px 0px 20px;text-align:center;margin:0;border:0;}
#form {}
#picture {float:left; margin:0; border: 0; padding: 20px;}
#message {margin:0; border: 0; padding: 20px;}
#sending {display: none;}
#progress {display:inline; border-collapse: separate; empty-cells: show;
           border-spacing:6px 0; padding:0;}
#progress td {height: 15px; width: 13px; background-color:#eee; 
              border: 1px solid #666; padding:0px;}
#linkurl {font-size:0.7em; text-align:right; padding: 10px; float:right}
--></style>'''



upload_page = '''<html><head><title>Upload files</title>
''' + style + '''
<script language="JavaScript">
function swap() {
   document.getElementById("form").style.display = "none";
   document.getElementById("sending").style.display = "block";
   update();
}
ncell = 4;
curcell = 0;
function update() {
   setTimeout(update, 300);
   e = document.getElementById("cell"+curcell);
   e.style.backgroundColor = "#eee";
   curcell = (curcell+1) %% ncell
   e = document.getElementById("cell"+curcell);
   e.style.backgroundColor = "#aaa";
}
function onunload() {
   document.getElementById("form").style.display = "block";
   document.getElementById("sending").style.display = "none";	  
}
</script></head><body>
  <div id="wrap">
    <div id="wrapform">
      <div id="form">
	<form method="post" enctype="multipart/form-data" action="">
	  <input name="upfile" type="file">
	  <input value="Send" onclick="swap()" type="submit">
	</form>
      </div>
      <div id="sending"> Sending &nbsp;
        <table id="progress"><tr>
          <td id="cell0"/><td id="cell1"/><td id="cell2"/><td id="cell3"/>
        </tr></table>
      </div>
    </div>
  </div>
</div></body></html>
'''

success_page = '''
<html>
<head><title>File sent!</title>
''' + style + '''
</head>
<body>
  <div id="wrap">
    <div id="wrapform">
      File received correctly!
      <a href="javascript:history.back()">Send another file</a>
    </div>
  </div>
</body>
</html>
'''

error_page = '''
<html>
<head><title> Error! </title>
''' + style + '''
</head>
<body>
  <div id="wrap">
    <div id="wrapform">
      There has been a problem
      <a href="javascript:history.back()"> Retry </a>
    </div>
  </div>
</body>
</html>
''' 



templates = {'main': default_page, 'upload': upload_page,
             'success': success_page, 'error': error_page}

class SendHandler(BaseHTTPRequestHandler):
    '''Custom handler for http request'''
    def html(self, page):
        return templates[page]
        
    def do_GET(self):
        if self.path[1:] in self.server.files: #File found
            filename = self.server.files[self.path[1:]]
            size = os.path.getsize(filename)
            file_to_send = open(filename)
            self.send_response(200)
            self.send_header('Content-length', size)
            self.end_headers()
            self.wfile.write(file_to_send.read())
        elif self.path[1:] == 'send': #Upload
            self.send_response(200)
            self.send_header('Content type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html('upload'))
        else: #File not found
            self.send_response(200)
            self.send_header('Content type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html('main'))
        return

    def do_POST(self):
        try:
            self.log_request()
            
            # -- Set up environment for cgi.FieldStorage
            env = {}
            env['REQUEST_METHOD'] = self.command
            if self.headers.typeheader is None:
                env['CONTENT_TYPE'] = self.headers.type
            else:
                env['CONTENT_TYPE'] = self.headers.typeheader

            # -- Save file (numbered to avoid overwriting, ex: foo-3.png)
            form = cgi.FieldStorage(fp = self.rfile, environ = env);
            fileitem = form['upfile']
            if fileitem.filename == "":
                raise Exception("Empty filename")
            localfilename = os.path.join(self.server.upload_dir, fileitem.filename)
            root, ext = os.path.splitext(localfilename)
            i = 1
            # race condition, but hey...
            while (os.path.exists(localfilename)): 
                localfilename = "%s-%d%s" % (root, i, ext)
                i = i+1
            print 'opening...'
            fout = file(localfilename, 'wb')
            print 'copying...'
            shutil.copyfileobj(fileitem.file, fout)
            print 'close!'
            fout.close()
            self.log_message("Received: %s", fileitem.filename)

            # -- Reply
            self.send_response(200)
            self.send_header('Content-type','text/html; charset=utf-8')
            self.end_headers()
            self.wfile.write(self.html("success").encode('utf-8'))
        except Exception, e:
            self.log_message(str(e))
            self.send_response(200)
            self.send_header('Content-type','text/html; charset=utf-8')
            self.end_headers()
            self.wfile.write(self.html("error").encode('utf-8'))




class SendServer(HTTPServer):
    '''custom server'''
    def __init__(self, *args, **kwargs):
        '''constructor'''
        HTTPServer.__init__(self, *args, **kwargs)
        self.files = {}
        self.upload_dir = None
    
    def set_upload_dir(self, dir):
        dir = os.path.expanduser(dir)
        print 'setting to', dir
        self.upload_dir = os.path.join(dir, 'uploads')
        try:
            os.mkdir(self.upload_dir)
        except OSError, e:
            if e.errno == 17:
                pass
            else:
                print e
                self.upload_dir = None
            
    def add_file(self, file_to_send):
        '''"send" a file'''
        while True:
            new_id = id_generator(6)
            if new_id not in self.files:
                break

        uri = '%s/%s' % (new_id, os.path.basename(file_to_send).replace(' ', '_'))

        self.files[uri] = file_to_send
        return uri

def id_generator(length):
    alphabet= '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    result = ''
    for i in range(length):
        result = '%s%s' % (result, random.choice(alphabet))
    return result
        


class MainClass(Plugin.Plugin):
    '''Main plugin class'''
    
    description = _('Send files through a web server (faster)')
    authors = { 'BoySka' : 'boyska gmail com'}
    website = 'emesene.org'
    displayName = _('Web Sender')
    name = 'WebSender'
    def __init__(self, controller, msn):
        '''Contructor'''
        Plugin.Plugin.__init__(self, controller, msn)
        self.description = _('Send files through a web server (faster)')
        self.authors = { 'BoySka' : 'boyska gmail com'}
        self.website = 'emesene.org'
        self.displayName = _('Web Sender')
        self.name = 'WebSender'

        self.controller = controller
        self.msn = msn
        
        self.config = self.controller.config
        self.config.readPluginConfig(self.name)
        self.port = int(self.config.getPluginValue(self.name, 'port', 8000))

        self.buttons = {} 

        self.signals = {}
        
        self.enabled = False
        self.server = None
        
    def start(self):
        '''start the plugin'''
        print 'server started'
        if not self.start_server():
            print 'WebSender:, error when starting (probably it was aborted)'
            self.enabled = False
            return

        self.signals['open_conv'] = self.controller.conversationManager.\
            connect('new-conversation-ui', self.add_button)
        self.signals['close_conv'] = self.controller.conversationManager.\
            connect('close-conversation-ui', self.remove_button)

        self.controller.Slash.register('websend', self.slash_send_file,
                                       _('Send a file through a webserver'))
        print 'running in bg!', self.server
        for conversation in self.getOpenConversations():
            self.add_button(conversation=conversation)

        self.enabled = True

    def stop(self):
        '''stop the plugin'''
        self.enabled = False
        

        self.controller.Slash.unregister('websend')

        self.controller.conversationManager.disconnect(
            self.signals['open_conv'])
        self.controller.conversationManager.disconnect(
            self.signals['close_conv'])

        for conversation in self.buttons.keys():
            #conversation.ui.input.toolbar.remove(self.buttons[conversation])
            self.remove_button(conversation=conversation)
        self.buttons = {}

        self.server.server_close()
        print 'server stopped!'

    def check(self):
        return (True, 'Ok')

    def configure(self):
        old_port = self.port
        l = [Plugin.Option('port', int, 'Port server will listen to:', 'Port',\
                  self.port)]
        result = Plugin.ConfigWindow(_('WebSender configuration'), l).run()

        if result:
            try:
                port = int(self.config.setPluginValue(
                        self.name, 'port', result['port'].value))
            except:
                return False
            if old_port != port:
                self.stop()
                self.start()
        return True

    #Core methods (server, messages)
    def start_server(self):
        port = self.port
        while True:
            try:
                self.server = SendServer(('', port), SendHandler)
                self.server.set_upload_dir(self.controller.
                                           config.user['receivedFilesDir'])
                print 'new server: ', self.server
                if not self.server:
                    raise Exception
                break
            except:
                print 'WebSender: problem when initializing server'
                response = StartErrorDialog(port).run()
                print 'dialog returned:', response
                if response:
                    port = response
                    print 'new port: ', port
                else:
                    return False
        thread.start_new_thread(self.server.serve_forever, ())
        return True

    def send_file(self, filepath, conversation):
        print 'adding', filepath
        file_id = self.server.add_file(filepath)
        message = '%s%s' % (self.get_base_path(), file_id)
        conversation.sendMessage(message)

    def get_ip(self):
        url_list = ['http://checkip.dyndns.org/',
                    'http://www.showmyip.com/simple',
                    'http://www.whatismyip.org']
        for url in url_list:
            try:
                ip_page = urllib2.urlopen(url)
                data = ip_page.read()
                matcher = re.compile(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
                ip = matcher.search(data).group() 
                print url, 'returned', ip
                return ip
            except:
                pass

    
    def get_base_path(self):
        ip = self.get_ip()
        return 'http://%s:%d/' % (ip,self.server.server_port)

    #UI methods
    def slash_send_file(self, slash_action):
        self.send_file(slash_action.paramas, slash_action.conversation)


    def add_button(self, conversationmanager=None, conversation=None, win=None):
        try:
            button = gtk.ToolButton(gtk.image_new_from_file(
                    paths.CONFIG_DIR + os.path.sep + paths.PLUGINS_HOME +
                    os.path.sep + 'WebSender.png'), None)
        except: #paths, image not found...
            button = gtk.ToolButton(label='WS')
                
                                                        
        button.connect("clicked", self.on_button_clicked)
        button.conversation = conversation
        toolbar = conversation.ui.input.toolbar
        toolbar.add(button)
        button.show()
        toolbar.show_all()
        
        self.buttons[conversation] = button

    def remove_button(self, conversationmanager=None, 
                      conversation=None, win=None):
        toolbar = conversation.ui.input.toolbar
        button = self.buttons[conversation]
        self.buttons.pop(conversation)
        toolbar.remove(button)

    def on_button_clicked(self, widget):
        #Rewrite the FileChooserDialog as a new object
        dialog = SenderPanel()
        dialog.set_current_folder(os.path.expanduser('~'))
        
        response = dialog.run()

        if response[0] == 1:
            print 'dialog returned: ', response[1]
            self.send_file(response[1], widget.conversation)
        if response[0] == 2: #receive
            print 'receive'
            message = '%ssend' % (self.get_base_path(),)
            widget.conversation.sendMessage(message)
    def on_receive_clicked(self, widget, dialog):
        dialog.response(gtk.RESPONSE_APPLY)
                    
class SenderPanel(gtk.FileChooserDialog):
    def __init__(self):
        gtk.FileChooserDialog.__init__(self, _('WebSend file'),
                                       buttons = (gtk.STOCK_CANCEL,
                                                  gtk.RESPONSE_CANCEL,
                                                  gtk.STOCK_OPEN,
                                                  gtk.RESPONSE_OK,
                                                  _('Offer for receiving'),
                                                  gtk.RESPONSE_CLOSE))

    def run(self):
        response = gtk.FileChooserDialog.run(self)
        if response == gtk.RESPONSE_OK:
            filename = self.get_filename()
            self.destroy()
            return (1, filename)
        elif response == gtk.RESPONSE_CLOSE:
            self.destroy()
            return (2, None)
        else:
            self.destroy()
            return (False, None)
        
class StartErrorDialog(gtk.Dialog):
    def __init__(self, port):
        gtk.Dialog.__init__(self, _('Choose a port'), None,
                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                            (gtk.STOCK_OK, gtk.RESPONSE_OK,
                             gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE))

        self.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
        self.spin = gtk.SpinButton()
        self.spin.set_range(1025, 10000)
        self.spin.set_digits(0)
        self.spin.set_increments(1,1)
        self.spin.set_value(port)
        self.vbox.pack_start(self.spin, True, False)
        self.vbox.show_all()


    def set_value(self, value):
        self.spin.set_value(value)

    def run(self):
        '''Managing the open and close of the dialog'''
        response = gtk.Dialog.run(self)
        self.destroy()
        print 'response', response, gtk.RESPONSE_OK

        if response == gtk.RESPONSE_OK:
            return self.spin.get_value()
        else:
            return None
    
